package com.xinchuang.util;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

@Slf4j
public class Geometry {

    //static Double height = 1.1d;
    
    private static double lngLength = 111136;

    public static List<List<Double>> getRotateSqureBySimilar(Double lng0, Double lat0, Double length, Double width, Double degree, Double height) {
        List<List<Double>> points = new ArrayList<List<Double>>();
        List<Double> b = new ArrayList<Double>();
        List<Double> a = new ArrayList<Double>();
        List<Double> d = new ArrayList<Double>();
        List<Double> c = new ArrayList<Double>();
        // degree = degree / Math.PI;
        degree = Math.toRadians(degree);
        Double yRad = Math.toRadians(lat0);
        // b:
        Double bX = 0d;
        Double bY = 0d;
        bX = lng0 + (length / 2 * Math.sin(degree) + width / 2 * Math.cos(degree)) / (lngLength * Math.cos(yRad));
        bY = lat0 + (length / 2 * Math.cos(degree) - width / 2 * Math.sin(degree)) / lngLength;
        b.add(bX);
        b.add(bY);

        // a:
        Double aX = 0d;
        Double aY = 0d;
        aX = lng0 + (length / 2 * Math.sin(degree) - width / 2 * Math.cos(degree)) / (lngLength * Math.cos(yRad));
        aY = lat0 + (length / 2 * Math.cos(degree) + width / 2 * Math.sin(degree)) / lngLength;
        a.add(aX);
        a.add(aY);

        // d:
        Double dX = 0d;
        Double dY = 0d;
        dX = lng0 + (length / 2 * Math.sin(degree) + width / 2 * Math.cos(degree)) / (lngLength * Math.cos(yRad)) * (-1);
        dY = lat0 + (length / 2 * Math.cos(degree) - width / 2 * Math.sin(degree)) / lngLength * (-1);
        d.add(dX);
        d.add(dY);

        // c:
        Double cX = 0d;
        Double cY = 0d;
        cX = lng0 + (length / 2 * Math.sin(degree) - width / 2 * Math.cos(degree)) / (lngLength * Math.cos(yRad)) * (-1);
        cY = lat0 + (length / 2 * Math.cos(degree) + width / 2 * Math.sin(degree)) / lngLength * (-1);
        c.add(cX);
        c.add(cY);

//        List<Double> list = new ArrayList<>();
        points.add(getRadianHeight(a.get(0), a.get(1), height));
//        list.addAll(getRadian(b.get(0), b.get(1)));
        points.add(getRadianHeight(c.get(0), c.get(1), height));
//        list.addAll(getRadian(d.get(0), d.get(1)));
//        points.add(list);

        return points;
    }

    // 按长宽

    public static List<List<Double>> getRotateSqureByLengthWidth(Double lng0, Double lat0, Double length, Double width, Double degree) {
        List<List<Double>> points = new ArrayList<List<Double>>();
        Double divide = degree / 90;
        Double uXDouble = 0d;// 右象限点
        Double uYDouble = 0d;
        Double vXDouble = 0d;// 左象限点
        Double vYDouble = 0d;

        Double tempDegree = 0d;
        // 四点顺序是：b->c->d->a
        List<Double> b = new ArrayList<Double>();
        List<Double> c = new ArrayList<Double>();
        List<Double> d = new ArrayList<Double>();
        List<Double> a = new ArrayList<Double>();
        Double tempX = 0d;
        ;
        Double tempY = 0d;
        if (divide > 2 && divide <= 3) {
            degree = degree - 180;
            divide = 1.0d;
        }
        if (divide > 3 && divide <= 4) {
            degree = degree - 180;
            divide = 2.0d;
        }
        if (degree >= 0 && degree <= 90) {
            tempDegree = 90 - degree;
        }
        if (degree > 90 && degree <= 180) {
            tempDegree = degree - 90;
        }

        Double temp1 = length / 2 * Math.sin(tempDegree);
        Double temp2 = length / 2 * Math.cos(tempDegree);
        Double temp3 = width / 2 * Math.sin(tempDegree);
        Double temp4 = width / 2 * Math.cos(tempDegree);

        if (divide <= 1 || divide >= 0) {
            uYDouble = lat0 + temp1 / 111000;
            uXDouble = lng0 + temp2 / (111000 * Math.cos(uYDouble));
            vYDouble = lat0 - temp1 / 111000;
            vXDouble = lng0 - temp2 / (111000 * Math.cos(vYDouble));
        }
        if (divide > 1 && divide <= 2) {

            uYDouble = lat0 - temp1 / 111000;
            uXDouble = lng0 + temp2 / (111000 * Math.cos(uYDouble));
            vYDouble = lat0 + temp1 / 111000;
            vXDouble = lng0 - temp2 / (111000 * Math.cos(vYDouble));
        }

        // b:
        tempY = uYDouble + temp4 / 111000;
        tempX = uXDouble - temp3 / (111000 * Math.cos(tempY));
        b.add(tempX * Math.PI / 180.0);
        b.add(tempY * Math.PI / 180.0);

        // c:
        tempY = uYDouble - temp4 / 111000;
        tempX = uXDouble + temp3 / (111000 * Math.cos(tempY));
        c.add(tempX * Math.PI / 180.0);
        c.add(tempY * Math.PI / 180.0);

        // d:
        tempY = vYDouble - temp4 / 111000;
        tempX = vXDouble + temp3 / (111000 * Math.cos(tempY));
        d.add(tempX * Math.PI / 180.0);
        d.add(tempY * Math.PI / 180.0);

        // a:
        tempY = vYDouble + temp4 / 111000;
        tempX = vXDouble - temp3 / (111000 * Math.cos(tempY));
        a.add(tempX * Math.PI / 180.0);
        a.add(tempY * Math.PI / 180.0);

        points.add(b);
        points.add(c);
        points.add(d);
        points.add(a);
        return points;
    }

    // 按对角线
    public static List<List<Double>> getRotateSqure(Double lng0, Double lat0, Double length, Double width, Double degree) {
        // // 真方位角 = 坐标方位角+子午线收敛角（收敛角近似等于）
        // Double rFix = lng0+Math.sin(lat0);
        // if (lng0 > 0) {
        // degree += rFix;
        // } else {
        // degree -= rFix;
        // }
        Double divide = degree / 90;
        List<List<Double>> points = new ArrayList<List<Double>>();
        List<Double> leftTop = new ArrayList<Double>();
        List<Double> rightTop = new ArrayList<Double>();
        List<Double> rightBottom = new ArrayList<Double>();
        List<Double> leftBottom = new ArrayList<Double>();
        Double xDouble = 0d;
        Double yDouble = 0d;
        Double radX = 0d;
        Double radY = 0d;
        // 对角线与纬线夹角
        Double r0 = Math.atan(width / length);
        // 对角线一半的长度
        Double halfL = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(length / 2, 2));
        // Double halfL = l / 2;
        if (divide > 2 && divide <= 3) {
            degree = degree - 180;
            divide = 1.0d;
        }
        if (divide > 3 && divide <= 4) {
            degree = degree - 180;
            divide = 2.0d;
        }
        if (divide <= 1) {
            // 右上
            yDouble = lat0 + Math.cos(degree - r0) * halfL / 111000;
            xDouble = lng0 + Math.sin(degree - r0) * halfL / (111000 * Math.cos(yDouble));
            radX = xDouble * Math.PI / 180;
            radY = yDouble * Math.PI / 180;
            rightTop.add(radX);
            rightTop.add(radY);
            // 右下
            yDouble = lat0 + Math.sin(90 - degree - r0) * halfL / 111000;
            xDouble = lng0 + Math.cos(90 - degree - r0) * halfL / (111000 * Math.cos(yDouble));
            radX = xDouble * Math.PI / 180;
            radY = yDouble * Math.PI / 180;
            rightBottom.add(radX);
            rightBottom.add(radY);

            // 左下
            yDouble = lat0 - Math.cos(degree - r0) * halfL / 111000;
            xDouble = lng0 - Math.sin(degree - r0) * halfL / (111000 * Math.cos(yDouble));
            radX = xDouble * Math.PI / 180;
            radY = yDouble * Math.PI / 180;
            leftBottom.add(radX);
            leftBottom.add(radY);
            // 左上
            yDouble = lat0 - Math.sin(90 - degree - r0) * halfL / 111000;
            xDouble = lng0 - Math.cos(90 - degree - r0) * halfL / (111000 * Math.cos(yDouble));
            radX = xDouble * Math.PI / 180;
            radY = yDouble * Math.PI / 180;
            leftTop.add(radX);
            leftTop.add(radY);
        }
        if (divide > 1 && divide <= 2) {
            // 右上
            yDouble = lat0 - Math.sin(degree - r0 - 90) * halfL / 111000;
            xDouble = lng0 + Math.cos(degree - r0 - 90) * halfL / (111000 * Math.cos(yDouble));
            radX = xDouble * Math.PI / 180;
            radY = yDouble * Math.PI / 180;
            rightTop.add(radX);
            rightTop.add(radY);
            // 右下
            yDouble = lat0 - Math.cos(180 - degree - r0) * halfL / 111000;
            xDouble = lng0 + Math.sin(180 - degree - r0) * halfL / (111000 * Math.cos(yDouble));
            radX = xDouble * Math.PI / 180;
            radY = yDouble * Math.PI / 180;
            rightBottom.add(radX);
            rightBottom.add(radY);

            // 左下
            yDouble = lat0 + Math.sin(degree - r0 - 90) * halfL / 111000;
            xDouble = lng0 - Math.cos(degree - r0 - 90) * halfL / (111000 * Math.cos(yDouble));
            radX = xDouble * Math.PI / 180;
            radY = yDouble * Math.PI / 180;
            leftBottom.add(radX);
            leftBottom.add(radY);
            // 左上
            yDouble = lat0 + Math.cos(180 - degree - r0) * halfL / 111000;
            xDouble = lng0 - Math.sin(180 - degree - r0) * halfL / (111000 * Math.cos(yDouble));
            radX = xDouble * Math.PI / 180;
            radY = yDouble * Math.PI / 180;
            leftTop.add(radX);
            leftTop.add(radY);
        }
        points.add(rightTop);
        points.add(rightBottom);
        points.add(leftBottom);
        points.add(leftTop);
        return points;
    }

    // 方位角：矩形水平方向线与经线指北方向的夹角
    /**
     * 计算矩形按方位角旋转后的坐标
     */

    public static List<Double> getRotate(Double lng0, Double lat0, Double lng, Double lat, Double degree) {
        List<Double> point = new ArrayList<Double>();
        Double temp = degree - 90;
        degree = temp >= 0 ? temp : (temp + 360);
        Double x2 = Math.cos(degree) * (lng - lng0) - Math.sin(degree) * (lat - lat0) + lng0;
        Double y2 = Math.sin(degree) * (lng - lng0) + Math.cos(degree) * (lat - lat0) + lat0;
        // 其中，q表示矩形的旋转角度，x1表示矩形的原顶点横坐标，x0表示矩形的中心坐标
        point.add(x2);
        point.add(y2);
        return point;
    }

    /**
     * 根据经纬度计算弧度数
     * @param lng
     * @param lat
     * @return
     */
    public static List<Double> getRadian(Double lng, Double lat) {
        List<Double> point = new ArrayList<Double>();
        Double x = lng * Math.PI / 180.0;
        Double y = lat * Math.PI / 180.0;
        point.add(x);
        point.add(y);
        return point;
    }

    /**
     * 根据经纬度计算弧度数加高度
     * @param lng
     * @param lat
     * @return
     */
    public static List<Double> getRadianHeight(Double lng, Double lat, Double height) {
        log.info("****************" + lng + "**" + lat + "**" + height);
        List<Double> point = new ArrayList<Double>();
        Double x = lng * Math.PI / 180.0;
        Double y = lat * Math.PI / 180.0;
        point.add(x);
        point.add(y);
        point.add(height);
        return point;
    }

    /**
     * 根据矩形中心点经纬度及长宽计算边框四个点经纬度（顺时针）,并换算成弧度。
     * @param lng
     * @param lat
     * @param length
     * @param width
     * @return
     */
    public static List<List<Double>> getSqurePosition(Double lng, Double lat, Double length, Double width, Double degree) {
        List<List<Double>> positions = new ArrayList<List<Double>>();
        List<Double> pointDouble = new ArrayList<Double>();
        /*List<Double> mecator = SingleDamageCalculate.lonLat2Mercator(lng, lat);
        Double minX = mecator.get(0) - length / 2.0;
        Double maxX = mecator.get(0) + length / 2.0;
        Double minY = mecator.get(1) - width / 2.0;
        Double maxY = mecator.get(1) + width / 2.0;
        // 计算左上角经纬度
        List<Double> leftTopMecator = new ArrayList<Double>();
        leftTopMecator.add(minX);
        leftTopMecator.add(maxY);
        List<Double> leftTopLngLat = SingleDamageCalculate.mercator2lonLat(leftTopMecator);
        pointDouble.add(leftTopLngLat.get(0));
        pointDouble.add(leftTopLngLat.get(1));
        // 计算右下角经纬度
        List<Double> rightBottomMecator = new ArrayList<Double>();
        rightBottomMecator.add(maxX);
        rightBottomMecator.add(minY);
        List<Double> rightBottomLngLat = SingleDamageCalculate.mercator2lonLat(rightBottomMecator);
        pointDouble.add(rightBottomLngLat.get(0));
        pointDouble.add(rightBottomLngLat.get(1));
        // 组装四个点的弧度
        List<Double> leftTop = new ArrayList<Double>();
        List<Double> rightTop = new ArrayList<Double>();
        List<Double> rightBottom = new ArrayList<Double>();
        List<Double> leftBottom = new ArrayList<Double>();
        List<Double> temp = new ArrayList<Double>();
        // 第一个点 ：左上
        leftTop.add(leftTopLngLat.get(0) * Math.PI / 180);
        leftTop.add(leftTopLngLat.get(1) * Math.PI / 180);
        // 旋转
        temp = getRotate(lng, lat, leftTopLngLat.get(0), leftTopLngLat.get(1), degree);
        // 计算弧度数
        leftTop = getRadian(temp.get(0), temp.get(1));
        positions.add(leftTop);
        // 第二个点：右上
        rightTop.add(rightBottomLngLat.get(0) * Math.PI / 180);
        rightTop.add(leftTopLngLat.get(1) * Math.PI / 180);
        // 旋转
        temp = getRotate(lng, lat, rightBottomLngLat.get(0), leftTopLngLat.get(1), degree);
        // 计算弧度数
        rightTop = getRadian(temp.get(0), temp.get(1));
        positions.add(rightTop);
        // 第三个点：右下
        rightBottom.add(rightBottomLngLat.get(0) * Math.PI / 180);
        rightBottom.add(rightBottomLngLat.get(1) * Math.PI / 180);
        // 旋转
        temp = getRotate(lng, lat, rightBottomLngLat.get(0), rightBottomLngLat.get(1), degree);
        // 计算弧度数
        rightBottom = getRadian(temp.get(0), temp.get(1));
        positions.add(rightBottom);
        // 第四个点：左下
         leftBottom.add(leftTopLngLat.get(0) * Math.PI / 180);
         leftBottom.add(rightBottomLngLat.get(1) * Math.PI / 180);
        // 旋转
        temp = getRotate(lng, lat, leftTopLngLat.get(0), rightBottomLngLat.get(1), degree);
        // 计算弧度数
        leftBottom = getRadian(temp.get(0), temp.get(1));*/

        // 组装四个点
        List<Double> leftTop = new ArrayList<Double>();
        List<Double> rightTop = new ArrayList<Double>();
        List<Double> rightBottom = new ArrayList<Double>();
        List<Double> leftBottom = new ArrayList<Double>();
        List<Double> temp = new ArrayList<Double>();

        Double latTemp = 0d;
        Double lngTemp = 0d;
        latTemp = width / 2 / 111000;
        lngTemp = length / 2 / (111000 * Math.cos(latTemp));

        leftTop.add(lng - lngTemp);
        leftTop.add(lat + latTemp);
        // 旋转
        temp = getRotate(lng, lat, leftTop.get(0), leftTop.get(1), degree);
        // 计算弧度数
        leftTop = getRadian(temp.get(0), temp.get(1));
        positions.add(leftTop);

        rightTop.add(lng + lngTemp);
        rightTop.add(lat + latTemp);
        // 旋转
        temp = getRotate(lng, lat, rightTop.get(0), rightTop.get(1), degree);
        // 计算弧度数
        rightTop = getRadian(temp.get(0), temp.get(1));
        positions.add(rightTop);

        rightBottom.add(lng + lngTemp);
        rightBottom.add(lat - latTemp);
        // 旋转
        temp = getRotate(lng, lat, rightBottom.get(0), rightBottom.get(1), degree);
        // 计算弧度数
        rightBottom = getRadian(temp.get(0), temp.get(1));
        positions.add(rightBottom);

        leftBottom.add(lng - lngTemp);
        leftBottom.add(lat - latTemp);
        // 旋转
        temp = getRotate(lng, lat, leftBottom.get(0), leftBottom.get(1), degree);
        // 计算弧度数
        leftBottom = getRadian(temp.get(0), temp.get(1));
        positions.add(leftBottom);

        return positions;
    }

    /**
     * 根据圆形中心点及半径，计算圆上一点经纬度，并返回两点经纬度,并换算成弧度。
     * @param lng
     * @param lat
     * @param radius
     * @return
     */
    public static List<List<Double>> getCirclePosition(Double lng, Double lat, Double radius,Double height) {
        List<List<Double>> positions = new ArrayList<List<Double>>();
        // 中心点弧度
        List<Double> centerPoint = new ArrayList<Double>();
        centerPoint.add(lng * Math.PI / 180);
        centerPoint.add(lat * Math.PI / 180);
        centerPoint.add(height);
        positions.add(centerPoint);
        // 计算圆上一点
        List<Double> pointOnCircle = new ArrayList<Double>();
        // 与中心点同经度，但是纬度增长方向上距离为radius的点
        Double pointOnCircleLng = lng;
        // 经线上，1纬度为111千米
        Double pointOnCircleLat = lat + radius / 111000;
        // 转为弧度
        pointOnCircle.add(pointOnCircleLng * Math.PI / 180);
        pointOnCircle.add(pointOnCircleLat * Math.PI / 180);
        pointOnCircle.add(height);
        positions.add(pointOnCircle);
        return positions;
    }

    /**
     * 根据环形内外圆半径，中心点经纬度，计算相对圆的圆上一点经纬度，并返回两点经纬度,并换算成弧度。
     * @param lng
     * @param lat
     * @param innerRadius
     * @param outterRadius
     * @return
     */
    public static List<List<Double>> getRingPosition(Double lng, Double lat, Double innerRadius, Double outterRadius,Double height) {
        List<List<Double>> positions = new ArrayList<List<Double>>();
        // 中心点弧度
        List<Double> centerPoint = new ArrayList<Double>();
        centerPoint.add(lng * Math.PI / 180);
        centerPoint.add(lat * Math.PI / 180);
        centerPoint.add(height);
        positions.add(centerPoint);
        // 计算相对圆半径
        Double radius = innerRadius + (outterRadius - innerRadius) / 2;
        // 计算圆上一点
        List<Double> pointOnCircle = new ArrayList<Double>();
        // 与中心点同经度，但是纬度增长方向上距离为radius的点
        Double pointOnCircleLng = lng;
        // 经线上，1纬度为111千米
        Double pointOnCircleLat = lat + radius / 111000;
        // 转为弧度
        pointOnCircle.add(pointOnCircleLng * Math.PI / 180);
        pointOnCircle.add(pointOnCircleLat * Math.PI / 180);
        pointOnCircle.add(height);
        positions.add(pointOnCircle);
        return positions;
    }
}
